﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using Algoritmia.Problems.ShortestPaths;
using Algoritmia.Problems.ShortestPaths.Positive;
using Demos.Problems;

namespace Demos.ShortestPaths.Dijkstra
{
    internal class ViewModel : INotifyPropertyChanged
    {
        private const int RadioVertice = 15;
        private readonly Dictionary<ElementoVisual, int> _aristasVisuales;
        private readonly Dictionary<ElementoVisual, int> _verticesVisuales;
        private MapGenerator _generador;
        private ObservableCollection<ElementoVisual> _mapa;
        private int _maxAristas;
        public bool _nuevoVertice;
        private int _numeroVertices;

        public ObservableCollection<ElementoVisual> Mapa
        {
            get { return _mapa; }
            set
            {
                _mapa = value;
                NotifyPropertyChanged("Mapa");
            }
        }

        public string NumeroVertices
        {
            get { return _numeroVertices.ToString(); }
            set
            {
                try
                {
                    _numeroVertices = int.Parse(value);
                    NotifyPropertyChanged("NumeroVertices");
                }
                catch (FormatException)
                {
                    MessageBox.Show("Error de formato", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }

        public string MaximasAristas
        {
            get { return _maxAristas.ToString(); }
            set
            {
                try
                {
                    _maxAristas = int.Parse(value);
                    NotifyPropertyChanged("MaximasAristas");
                }
                catch (FormatException)
                {
                    MessageBox.Show("Error de formato", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }

        public string ContenidoBoton { get; set; }

        public double MaxX { get; set; }
        public double MaxY { get; set; }

        private double _x;
        public double X
        {
            get { return _x; }
            set
            {
                _x = value;
                NotifyPropertyChanged("X");
            }
        }

        private double _y;
        public double Y
        {
            get { return _y; }
            set
            {
                _y = value;
                NotifyPropertyChanged("Y");
            }
        }

        private string _verticeSeleccionado;
        public string VerticeSeleccionado
        {
            get { return _verticeSeleccionado; }
            set
            {
                _verticeSeleccionado = value;
                NotifyPropertyChanged("VerticeSeleccionado");
            }
        }

        private string _aristaSeleccionado;
        public string AristaSeleccionado
        {
            get { return _aristaSeleccionado; }
            set
            {
                _aristaSeleccionado = value;
                NotifyPropertyChanged("AristaSeleccionado");
            }
        }

        public ICommand CrearMapaAleatorioCommand { get; private set; }
        public ICommand AñadirVerticesCommand { get; private set; }
        public ICommand AñadirVerticeCommand { get; private set; }
        public ICommand OcultarPesosCommand { get; private set; }
        public ICommand BorrarElementoCommand { get; private set; }
        public ICommand CrearNuevaAristaCommand { get; private set; }
        public ICommand BorrarMapaCommand { get; private set; }
        public ICommand SeleccionarVerticeCommand { get; private set; }
        public ICommand SeleccionarAristaCommand { get; private set; }
        public ICommand BorrarSeleccionCommand { get; private set; }

        public ICommand VerSolucionCommand { get; private set; }
        public ICommand ResolverCommand { get; private set; }
        public ICommand AvanzarCommand { get; private set; }
        public ICommand PararCommand { get; private set; }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        private void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        private List<Tuple<Vertice, Vertice>> _frontera;


        public ViewModel()
        {
            Mapa = new ObservableCollection<ElementoVisual>();
            _frontera = new List<Tuple<Vertice, Vertice>>();
            _verticesVisuales = new Dictionary<ElementoVisual, int>();
            _aristasVisuales = new Dictionary<ElementoVisual, int>();
            _generador = new MapGenerator(0, 0, 0, 0, RadioVertice, RadioVertice);
            NumeroVertices = "3";
            MaximasAristas = "1";
            _generador.NuevoElemento += CrearElemento;

            MaxX = 300;
            MaxY = 200;

            CrearMapaAleatorioCommand = new RelayCommand(CrearMapa, () => Mapa.Count == 0);
            AñadirVerticesCommand = new RelayCommand(AñadirVertices, () => true);
            AñadirVerticeCommand = new RelayCommand(AñadirVertice, () => _nuevoVertice);
            OcultarPesosCommand = new RelayCommand(OcultarPesos, 
                                  () => Mapa.Any(elementoVisual => elementoVisual is AristaVisual && elementoVisual.Etiqueta != ""));
            BorrarElementoCommand = new RelayCommand(EliminarElemento, () => _generador.AristaSeleccionada >= 0
                                                                             || _generador.Origen != null 
                                                                             || _generador.Destino != null);
            CrearNuevaAristaCommand = new RelayCommand(AñadirArista,
                                                       () => _generador.Origen != null && _generador.Destino != null);
            BorrarMapaCommand = new RelayCommand(BorrarMapa, () => Mapa.Count > 0);
            SeleccionarVerticeCommand = new RelayCommand(SeleccionarVertice, () => !_nuevoVertice);
            SeleccionarAristaCommand = new RelayCommand(SeleccionarArista, () => Mapa.Count > 0);
            BorrarSeleccionCommand = new RelayCommand(ResetOrigenDestino, 
                                                      () => _generador.Origen != null || _generador.Destino != null
                                                            || _generador.AristaSeleccionada >= 0);

            VerSolucionCommand = new RelayCommand(Solucionar,
                                                  () => _generador.Origen != null && _generador.Destino != null);
            ResolverCommand = new RelayCommand(Resolver, () => _generador.Origen != null && _generador.Destino != null
                                                               && _frontera.Count == 0);
            AvanzarCommand = new RelayCommand(Step, () => _frontera.Count > 0);
            PararCommand = new RelayCommand(() => _frontera.Clear(), () => _frontera.Count > 0);

            _nuevoVertice = false;
            ContenidoBoton = "Añadir vértices";
            NotifyPropertyChanged("ContenidoBoton");



            //mapa prefabricado
            _generador = new MapGenerator(0, 0, 0, 0, RadioVertice, RadioVertice);
            _generador.NuevoElemento += CrearElemento;
            _generador.NuevoGrafoPrefabricado();
            _generador.CrearMapa();
        }


        private void BorrarMapa()
        {
            Mapa.Clear();
            ResetOrigenDestino();
            _generador.BorrarMapa();
        }

        private void OcultarPesos()
        {
            foreach (var elementoVisual in Mapa)
            {
                if (elementoVisual is AristaVisual)
                {
                    elementoVisual.Etiqueta = "";
                }
            }
        }

        private void AñadirVertice()
        {
            _generador.AñadirVertice(X, Y);
        }

        private void AñadirArista()
        {
            _generador.AñadirArista();
            ResetOrigenDestino();

            Mapa.Clear();
            _verticesVisuales.Clear();
            _aristasVisuales.Clear();
            _generador.CrearMapa();
        }

        private void AñadirVertices()
        {
            ContenidoBoton = _nuevoVertice ? "Añadir vértices" : "Terminado";
            NotifyPropertyChanged("ContenidoBoton");
            _nuevoVertice = _nuevoVertice ? false : true;
        }

        private void CrearMapa()
        {
            _generador = new MapGenerator(int.Parse(NumeroVertices), int.Parse(MaximasAristas), MaxX - RadioVertice * 2, MaxY - RadioVertice * 2, RadioVertice, RadioVertice);
            _generador.NuevoElemento += CrearElemento;
            _generador.NuevoGrafoYWeightingFunction();
            _generador.CrearMapa();
        }

        private void CrearElemento(Elemento elem, List<double> valores, string etiqueta, int indice)
        {
            switch (elem)
            {
                case Elemento.Vertice:
                    Mapa.Add(new VerticeVisual(
                        valores[0] - RadioVertice, valores[1] - RadioVertice, RadioVertice*2, RadioVertice*2, etiqueta,
                        "V" + indice, Brushes.LightGray));
                    _verticesVisuales[Mapa.Last()] = indice;
                    break;
                case Elemento.Arista:
                    Mapa.Add(new AristaVisual(valores[0], valores[1], 0, 0, valores[2] - valores[0], valores[3] - valores[1], 
                        "A"+indice, Brushes.Black, etiqueta));
                    _aristasVisuales[Mapa.Last()] = indice; 
                    break;
            }
        }

        private void SeleccionarArista()
        {
            if (PararCommand.CanExecute(this)) PararCommand.Execute(this);

            foreach (AristaVisual arista in _aristasVisuales.Keys)
            {
                arista.Color = Brushes.Black;
            }

            foreach (var elementoVisual in Mapa)
            {
                if (elementoVisual.Identificador == AristaSeleccionado)
                {
                    _generador.AristaSeleccionada = _aristasVisuales[elementoVisual];
                    elementoVisual.Color = Brushes.YellowGreen;
                    break;
                }
            }
        }
        
        private void SeleccionarVertice()
        {
            if (PararCommand.CanExecute(this)) PararCommand.Execute(this);

            foreach (var elementoVisual in Mapa)
            {
                if (elementoVisual.Identificador == VerticeSeleccionado)
                {
                    if (_generador.Origen == null)
                    {
                        _generador.Origen = _generador.Vertices[_verticesVisuales[elementoVisual]];
                        elementoVisual.Color = Brushes.Yellow;
                    }
                    else if (_generador.Destino == null)
                    {
                        _generador.Destino = _generador.Vertices[_verticesVisuales[elementoVisual]];
                        elementoVisual.Color = Brushes.LightGreen;
                    }
                    else
                    {
                        ResetOrigenDestino();
                        SeleccionarVertice();
                    }
                    break;
                }
            }
        }

        public void ElementoModificado(string c, Object o)
        {
            _generador.Etiquetas[o] = c;
        }

        private void ResetOrigenDestino()
        {
            foreach (VerticeVisual vertice in _verticesVisuales.Keys)
            {
                vertice.Color = Brushes.LightGray;
            }
            foreach (AristaVisual arista in _aristasVisuales.Keys)
            {
                arista.Color = Brushes.Black;
            }

            _generador.Origen = null;
            _generador.Destino = null;
            _generador.AristaSeleccionada = -1;
        }

        private void EliminarElemento()
        {
            Mapa.Clear();
            _verticesVisuales.Clear();
            _aristasVisuales.Clear();
            if (_generador.AristaSeleccionada >= 0)
            {
                _generador.EliminarArista();
            }
            if (_generador.Origen != null || _generador.Destino != null)
            {
                _generador.EliminarVertice();
            }
            _generador.CrearMapa();
            ResetOrigenDestino();
        }

        private void Solucionar()
        {
            var finder = new DijkstraShortestPathsFinder<Vertice>();
            IEnumerable<Vertice> camino = new List<Vertice>();
            try
            {
                camino = finder.ShortestPath(_generador.Grafo, _generador.WeightingFunction,
                                                              _generador.Origen, _generador.Destino);
            }
            catch (ArgumentException)
            {
                MessageBox.Show("Sin solución", "Aviso", MessageBoxButton.OK, MessageBoxImage.Information);
            }
            var solucion = camino.ToList();
            for (int i = 0; i < solucion.Count; i++)
            {
                if (i != 0)
                {
                    DestacarArista(new Tuple<Vertice, Vertice>(solucion[i - 1], solucion[i]));
                }
            }
        }

        private void Resolver()
        {
            _frontera.Clear();
            var finder = new ObservableDijkstra();
            finder.AñadirFrontera += (arista) => _frontera.Add(arista);
            try
            {
                foreach (var x in finder.SomeToSomeBackpointers(_generador.Grafo, _generador.WeightingFunction,
                                                            new List<Vertice> { _generador.Origen },
                                                            new List<Vertice> { _generador.Destino }))
                {
                }
            }
            catch (ArgumentException)
            {
                MessageBox.Show("Sin solución", "Aviso", MessageBoxButton.OK, MessageBoxImage.Information);
            }
        }

        private void DestacarArista(Tuple<Vertice, Vertice> arista)
        {
            for (var i = 0; i < _generador.Aristas.Count; i++)
            {
                if (arista.Item1 == _generador.Aristas[i].Item1 && arista.Item2 == _generador.Aristas[i].Item2)
                {
                    foreach (var aristaVisual in _aristasVisuales.Keys)
                    {
                        if (_aristasVisuales[aristaVisual] == i)
                        {
                            aristaVisual.Color = Brushes.Red;
                        }
                    }
                }
            }
        }

        private void DestacarAristaFrontera(Tuple<Vertice, Vertice> arista)
        {
            for (var i = 0; i < _generador.Aristas.Count; i++)
            {
                if (arista.Item1 == _generador.Aristas[i].Item1 && arista.Item2 == _generador.Aristas[i].Item2)
                {
                    foreach (var aristaVisual in _aristasVisuales.Keys)
                    {
                        if (_aristasVisuales[aristaVisual] == i)
                        {
                            aristaVisual.Color = Brushes.LightBlue;
                        }
                    }
                }
            }
        }

        private void Step()
        {
            DestacarAristaFrontera(_frontera.First());
            _frontera.RemoveAt(0);
        }
    }
}